//
// internal.h
//
// (c) 2008 why the lucky stiff, the freelance professor
//
#ifndef POTION_INTERNAL_H
#define POTION_INTERNAL_H

// size_t 等标识符需要
#include <stdio.h>

struct Potion_State;

typedef unsigned char u8;

// 为结构类型 T 分配内存(在新生代)，返回该类型地址指针；
//    初始化 .vt=V 和 .uniq ，按需 GC 。
#define PN_ALLOC(V,T)        (T *)potion_gc_alloc(P, V, sizeof(T))
// 为结构类型 T(C 为其柔性数组字段内存尺寸) 分配内存(在新生代)，返回该类型地址指针；
//    初始化 .vt=V 和 .uniq ，按需 GC 。
#define PN_ALLOC_N(V,T,C)    (T *)potion_gc_alloc(P, V, sizeof(T)+C)
#define PN_CALLOC(V,T)   (T *)potion_gc_calloc(P, V, sizeof(T))
// 未使用 #define PN_CALLOC_N(V,T,C)   (T *)potion_gc_calloc(P, V, sizeof(T)+C)
// 类似 PN_ALLOC_N ； X 是 T * ；如 !X 则只是 PN_ALLOC_N ，非空且容量足够返回 X ，
//    否则新分配并拷贝，旧对象 X 则变成 PNFwd 记录到 GC 中，并指向新对象。
#define PN_REALLOC(X,V,T,N)  (X)=(T *)potion_gc_realloc(P, V, (struct PNObject *)(X), sizeof(T) + N)
// 创建 PNData ， .vt=PN_TUSER ， N 个类型 T ；只用在哈希和测试代码中。
#define PN_DALLOC_N(T,N)     potion_data_alloc(P, sizeof(T)*N)

// 内存清零：地址 X 处的一个类型 T 。
#define PN_MEMZERO(X,T)      memset((X), 0, sizeof(T))
// 内存清零：地址 X 处的 N 个类型 T 。
#define PN_MEMZERO_N(X,T,N)  memset((X), 0, sizeof(T)*(N))
// 内存拷贝：地址 Y 到 X 处的一个类型 T 。
#define PN_MEMCPY(X,Y,T)     memcpy((void *)(X), (void *)(Y), sizeof(T))
// 内存拷贝：地址 Y 到 X 处的 N 个类型 T 。
#define PN_MEMCPY_N(X,Y,T,N) memcpy((void *)(X), (void *)(Y), sizeof(T)*(N))

#ifndef min
  #define min(a, b) ((a) <= (b) ? (a) : (b))
#endif

#ifndef max
  #define max(a, b) ((a) >= (b) ? (a) : (b))
#endif

#define TYPE_BATCH_SIZE 4096

// 为结构类型 T(S 为其柔性数组成员数量) 分配内存(在新生代)，地址赋给指针 N ；
//    初始化 .vt=V, .uniq, .siz=SIZE, .len=0 ，按需 GC 。参考 PN_ALLOC_N 。
#define PN_FLEX_NEW(N, V, T, S) \
  (N) = PN_ALLOC_N(V, T, (sizeof(*(N)->ptr) * S)); \
  (N)->siz = sizeof(*(N)->ptr) * S; \
  (N)->len = 0

// 检查结构类型 T(S 为其柔性数组成员数量) 的变量 N 是否可再容纳 X 个元素；。
//    按需扩展内存（以 S 个元素为单位递增），按需 GC 。参考 PN_FLEX_NEW 。
#define PN_FLEX_NEEDS(X, N, V, T, S) ({ \
  PN_SIZE capa = (N)->siz / sizeof(*(N)->ptr); \
  if (capa < (N)->len + X) { \
    while (capa < (N)->len + X) \
      capa += S; \
    capa = sizeof(*(N)->ptr) * capa; \
    PN_REALLOC(N, V, T, capa); \
    (N)->siz = capa; \
  } \
})

// N 个字节的字符串 X 转为十或十六进制 B 整数，无前缀。
#define PN_ATOI(X,N,B) ({ \
  char *Ap = X; \
  int Ai = 0; \
  size_t Al = N; \
  while (Al--) { \
    if ((*Ap >= '0') && (*Ap <= '9')) \
      Ai = (Ai * B) + (*Ap - '0'); \
    else if ((*Ap >= 'A') && (*Ap <= 'F')) \
      Ai = (Ai * B) + ((*Ap - 'A') + 10); \
    else if ((*Ap >= 'a') && (*Ap <= 'f')) \
      Ai = (Ai * B) + ((*Ap - 'a') + 10); \
    else break; \
    Ap++; \
  } \
  Ai; \
})

size_t potion_cp_strlen_utf8(const char *);
void *potion_mmap(size_t length);
void *potion_mprotect(void *addr, size_t length, const char exec);
int potion_munmap(void *mem, size_t len);
// 分配可执行内存。
#define PN_ALLOC_FUNC(size) potion_mmap(size)
#define PN_EXEC_FUNC(addr, size) potion_mprotect(addr, size, 1)
#define PN_WRITE_FUNC(addr, size) potion_mprotect(addr, size, 0)

//
// stack manipulation routines
//
#ifdef POTION_X86
  #if __WORDSIZE == 64
    #define PN_SAVED_REGS 5
    // 取得寄存器 rsp 的值
    #define POTION_ESP(p) __asm__("mov %%rsp, %0" : "=r" (*p))
    // 取得寄存器 rbp 的值
    #define POTION_EBP(p) __asm__("mov %%rbp, %0" : "=r" (*p))
  #endif
#else
  #define PN_SAVED_REGS 0
  __attribute__ ((noinline)) void potion_esp(void **);
  #define POTION_ESP(p) potion_esp((void **)p)
  #define POTION_EBP(p) potion_esp((void **)p)
#endif

#ifndef O_BINARY
  // 兼容 windows 平台
  #define O_BINARY 0
#endif

#if POTION_STACK_DIR > 0
  #define STACK_UPPER(a, b) a
#elif POTION_STACK_DIR < 0
  // 根据栈增长方向，取得地址较大的变量；b 先于 a 声明。
  #define STACK_UPPER(a, b) b
#endif

// 设置受保护的内存起始地址（ .birth_cur 新生代当前位置）。
#define GC_PROTECT(P) P->mem->protect = (void *)P->mem->birth_cur

#endif
